require 'fileutils'

class PromotionMail < ActiveRecord::Base
  include FileUtils
  include DeliveryStatus
  include Rails.application.routes.url_helpers

  belongs_to :created_by, class_name: 'Admin'
  belongs_to :delivered_by, class_name: 'Admin'
  has_many :deliveries
  has_many :bounces
  has_many :mail_opens
  has_many :mail_clicks
  has_one :report

  has_attached_file :attachment, url: '/assets/mails/:id/:id.:extension'
  validates_attachment_content_type :attachment,
                                    content_type: /gz|gzip|bz2?|bzip2?|rar|zip|7z|7zip/

  after_save :decompress_attachment

  def total
    deliveries.reduce(0){|sum, d| sum + d.total}
  end

  def sent
    deliveries.reduce(0){|sum, d| sum + d.sent}
  end

  def status
    @deliveries ||= deliveries.to_a
    if @deliveries.any?(&:sending?)
      DeliveryStatus::SENDING.to_s
    elsif @deliveries.any?(&:pause?)
      DeliveryStatus::PAUSE.to_s
    elsif @deliveries.any?(&:complete?)
      DeliveryStatus::COMPLETE.to_s
    else
      DeliveryStatus::NOT_STARTED.to_s
    end
  end

  def can_start?
    status.to_sym.in? [DeliveryStatus::NOT_STARTED, DeliveryStatus::COMPLETE]
  end

  def can_resume?
    @deliveries ||= deliveries.to_a
    !!deliveries.find{|d| d.status.to_sym == DeliveryStatus::PAUSE}
  end

  def can_pause?
    @deliveries ||= deliveries.to_a
    !!deliveries.find{|d| d.status.to_sym == DeliveryStatus::SENDING}
  end

  def can_stop?
    @deliveries ||= deliveries.to_a
    !!deliveries.find{|d| d.status.to_sym.in? [DeliveryStatus::SENDING, DeliveryStatus::PAUSE]}
  end

  def default_url_options
    {
        host: 'localhost',
        port: 3333,
        protocol: 'http'
    }
  end

  def save_with_macro_compilation
    transaction do
      save
      compile_macros!
      save
    end
  end

  def update_with_macro_compilation(attributes)
    transaction do
      update(attributes)
      compile_macros!
      save
    end
  end

  def compile_macros!
    {
        /\$\{(.+?)\}/ => proc{promotion_mail_mail_clicks_url(id, target: $1)},
        /\$UNSUBSCRIBE/ => proc{unsubscribe_users_url},
        /\$VIEW/ => proc{view_promotion_mail_url(id)}
    }.each do |pattern, value|
      self.html_part = html_part.gsub(pattern, &value)
      self.text_part = text_part.gsub(pattern, &value)
    end
  end

  def create_report
    return unless status == DeliveryStatus::COMPLETE.to_s
    return report if report

    delivered = deliveries.reduce({}){|h, d| h.merge!(d.domain_name => h[d.domain_name].to_i + d.delivered)}
    delivered['total'] = delivered.reduce(0){|sum, d| sum + d[1]}
    bounced = bounces.group(:domain_name).count
    bounced['total'] = bounced.reduce(0){|sum, b| sum + b[1]}
    arrived = delivered.map{|k, v| [k, v - bounced[k].to_i]}.to_h
    raw_opens = mail_opens.group(:ip).count
    opens = Hash.new(0)
    raw_opens.each do |ip, count|
      opens[geo(ip)] += count
    end
    opens['total'] = opens.reduce(0){|sum, o| sum + o[1]}
    raw_clicks = mail_clicks.group(:url, :ip).count
    clicks = {}
    raw_clicks.each do |key, count|
      url = key[0]
      province = geo(key[1])
      clicks[url] ||= Hash.new(0)
      clicks[url][province] += count
      clicks[url]['total'] += count
    end
    clicks['total'] = clicks.reduce(0){|sum, c| sum + c[1]['total']}

    content = {
        delivered: delivered,
        arrived: arrived,
        opens: opens,
        clicks: clicks
    }

    transaction do
      Report.create(promotion_mail: self, content: content.to_json)
      mail_opens.delete_all
      mail_clicks.delete_all
      bounces.delete_all
    end
    report
  end

  private
  def decompress_attachment
    return true if attachment.nil?
    directory = "public/#{File.dirname attachment.url}"
    mkdir_p directory unless File.directory? directory
    cd directory do
      filename = File.basename(attachment.url).split('?').first
      Archive.new(filename).extract
    end
  end

  def geo(ip)
    RestClient.get("http://ip.taobao.com/service/getIpInfo.php?ip=#{ip}") do |response|
      if response.code == 200
        h = MultiJson.load(response.body)  # Province
        if h['code'] == 0
          return h['data']['region']
        else
          return 'Unknown'
        end
      else
        return 'Unknown'
      end
    end
  end

end
